package com.infolab.aims.service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.infolab.aims.common.PaginationHelper;
import com.infolab.aims.common.QueryHelper;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.common.exception.DataStoreException;
import com.infolab.aims.domain.asset.AssetModel;
import com.infolab.aims.domain.purchase.PurchaseOrder;
import com.infolab.aims.domain.purchase.PurchaseOrderDetail;
import com.infolab.aims.domain.receive.DeliveryOrder;
import com.infolab.aims.domain.receive.DeliveryOrderComponent;
import com.infolab.aims.domain.receive.DeliveryOrderEquipment;
import com.infolab.aims.service.intf.DeliveryOrderService;

@Service
@Transactional
public class DeliveryOrderServiceImpl implements DeliveryOrderService {
	
	Log logger = LogFactory.getLog(getClass());
	@PersistenceContext
	private EntityManager em;
	
	
	private String getNextAssetTag(String assetType,String assetModelCode)
	{
		String assetTag="FA-"+assetType.substring(0, 1)+"-"+assetModelCode+"-"+new SimpleDateFormat("yyyyMMdd").format(new Date())+"-";

		Integer nextVal = (Integer) em.createNativeQuery("select nextval('AMSQ_CT_ASSETTAG') from dual").getSingleResult();

		return assetTag+String.format("%03d", nextVal.intValue());
	}

	public void createDeliveryOrder(DeliveryOrder deliveryOrder) {
		
		logger.info("Create Delivery Order "+  deliveryOrder.getDeliveryOrderCode());
		
		if(StringUtils.isNotEmpty(deliveryOrder.getDeliveryOrderCode()))
		{
			SearchCriteria searchCriteria = new SearchCriteria();
			searchCriteria.addSearchCriteria("deliveryOrderCode", deliveryOrder.getDeliveryOrderCode());
			DeliveryOrder tmp = findDeliveryOrder(searchCriteria);
			if(tmp!=null)
				throw new DataStoreException(true,"deliveryOrderCode");
		}
		
		DeliveryOrder newDeliveryOrder= new DeliveryOrder();
		
		deliveryOrder.setModifiedBy(deliveryOrder.getCreatedBy());
		deliveryOrder.setDateCreated(new Date());
		deliveryOrder.setDateModified(new Date());
		deliveryOrder.setDeliveryOrderStatus(DeliveryOrder.DELIVERYORDERSTATUS_CLOSED);
		
		BeanUtils.copyProperties(deliveryOrder,newDeliveryOrder);
		
		em.persist(newDeliveryOrder);
		
		updateDeliveryOrderItems(newDeliveryOrder);
		
		deliveryOrder.setDeliveryOrderId(newDeliveryOrder.getDeliveryOrderId());
	}

	public DeliveryOrder getDeliveryOrder(Integer deliveryOrderId) {
		
		DeliveryOrder deliveryOrder = em.find(DeliveryOrder.class, deliveryOrderId);

		if(deliveryOrder.isEquipmentDeliveryOrder())
		{
			Query query = em.createQuery("from DeliveryOrderEquipment where deliveryOrder.deliveryOrderId=:deliveryOrderId");
			query.setParameter("deliveryOrderId", deliveryOrderId);
			List<DeliveryOrderEquipment> deliveryOrderEquipments = query.getResultList();
			deliveryOrder.setDeliveryOrderEquipments(deliveryOrderEquipments);
		}
		else if(deliveryOrder.isComponentDeliveryOrder())
		{
			Query query = em.createQuery("from DeliveryOrderComponent where deliveryOrder.deliveryOrderId=:deliveryOrderId");
			query.setParameter("deliveryOrderId", deliveryOrderId);
			List<DeliveryOrderComponent> deliveryOrderComponents = query.getResultList();
			deliveryOrder.setDeliveryOrderComponents(deliveryOrderComponents);
		}
		
		return deliveryOrder;
		
	}

	public DeliveryOrder findDeliveryOrder(SearchCriteria searchCriteria) {
		List<DeliveryOrder> deliveryOrders = findDeliveryOrders(searchCriteria);
		if(deliveryOrders!=null && deliveryOrders.size()==1)
			return deliveryOrders.get(0);
		
		return null;
	}

	public List<DeliveryOrder> findDeliveryOrders(SearchCriteria searchCriteria) {
		if(searchCriteria==null) searchCriteria = new SearchCriteria();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<DeliveryOrder> cq = cb.createQuery(DeliveryOrder.class);
		
		Root<DeliveryOrder> root = cq.from(DeliveryOrder.class);
			
		
		List<Predicate> criteria = new ArrayList<Predicate>();
		
		if (searchCriteria.getSearchCriteria("deliveryOrderCode")!= null) {
			ParameterExpression<String> p = cb.parameter(String.class, "deliveryOrderCode");
			criteria.add(cb.equal(root.get("deliveryOrderCode"),  p));
		}
		
		/*if (searchCriteria.getSearchCriteria("accessDepartmentId")!= null) {
			ParameterExpression<Integer> p = cb.parameter(Integer.class, "accessDepartmentId");
			criteria.add(cb.or(cb.isNull(root.<Integer>get("accessDepartmentId")),cb.equal((root.<Integer>get("accessDepartmentId")), p)));
		}*/
		
		cq.select(root);
		if (criteria.size() == 0) {
			} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
			} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}
		
		Query query = em.createQuery(cq);
		
		if (searchCriteria.getSearchCriteria("deliveryOrderCode")!= null) {
			query.setParameter("deliveryOrderCode", searchCriteria.getSearchCriteria("deliveryOrderCode"));
		}
		/*if (searchCriteria.getSearchCriteria("accessDepartmentId")!= null) {
			query.setParameter("accessDepartmentId", searchCriteria.getSearchCriteria("accessDepartmentId"));
		}*/
		
		
		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());
		
		return query.getResultList();
	}

	public SearchResult searchDeliveryOrder(SearchCriteria searchCriteria) {
		
		if(searchCriteria.getCurPage()==null)
			searchCriteria.setCurPage(1);
		
		DeliveryOrderPaginationHelper helper = new DeliveryOrderPaginationHelper();
		
		return helper.getSearchResultFromQueryHelper(em, searchCriteria);
	}

	public void updateDeliveryOrder(DeliveryOrder deliveryOrder) {
		
		logger.info("Update Delivery Order "+  deliveryOrder.getDeliveryOrderCode());
		
		DeliveryOrder newDeliveryOrder = em.find(DeliveryOrder.class, deliveryOrder.getDeliveryOrderId());
		
		deliveryOrder.setDateModified(new Date());
		
		BeanUtils.copyProperties(deliveryOrder,newDeliveryOrder);
		
		em.merge(newDeliveryOrder);
		
		updateDeliveryOrderItems(deliveryOrder);
		
		
		
	}
	
	private void updateDeliveryOrderItems(DeliveryOrder deliveryOrder)
	{
		PurchaseOrder purchaseOrder = deliveryOrder.getPurchaseOrder();
		
		if(deliveryOrder.isEquipmentDeliveryOrder())
		{
			List<DeliveryOrderEquipment> deliveryOrderEquipments = deliveryOrder.getDeliveryOrderEquipments();
			
			if(deliveryOrderEquipments!=null) {
				for (int i = 0; i < deliveryOrderEquipments.size(); i++) {
					DeliveryOrderEquipment deliveryOrderEquipment= deliveryOrderEquipments.get(i);
					
					deliveryOrderEquipment.setDeliveryOrder(deliveryOrder);
					
					if(deliveryOrderEquipment.getDeliveryOrderEquipmentId()==null)
					{
						if(deliveryOrderEquipment.getAssetModel()==null)
							logger.info("Nothing to do");
						else
						{
							if(purchaseOrder.isNewPurchaseOrder())
							{
								PurchaseOrderDetail purchaseOrderDetail = purchaseOrder.findPurchaseOrderDetails(deliveryOrderEquipment.getAssetModel());
								if(purchaseOrderDetail!=null)
								{
									deliveryOrderEquipment.setUnitPrice(purchaseOrderDetail.getUnitPrice());
									deliveryOrderEquipment.setAssetTag(getNextAssetTag(AssetModel.ASSETTYPE_EQUIPMENT, deliveryOrderEquipment.getAssetModel().getAssetModelCode()));
									em.persist(deliveryOrderEquipment);
								}
								else
									logger.warn("Asset Model " +deliveryOrderEquipment.getAssetModel()+ " is not available in PO items");
							}
							else if(purchaseOrder.isRepairPurchaseOrder())
							{
								if(deliveryOrderEquipment.isActive())
									em.persist(deliveryOrderEquipment);
							}
						}
					}
					else
					{
						if(deliveryOrderEquipment.isDeleted())
						{
							deliveryOrderEquipment = em.getReference(DeliveryOrderEquipment.class,deliveryOrderEquipment.getDeliveryOrderEquipmentId());
							em.remove(deliveryOrderEquipment);
						}
						else
							em.merge(deliveryOrderEquipment);
					}
				}
			}
		}
		else if(deliveryOrder.isComponentDeliveryOrder())
		{
			List<DeliveryOrderComponent> deliveryOrderComponents = deliveryOrder.getDeliveryOrderComponents();
			if(deliveryOrderComponents!=null) {
				for (int i = 0; i < deliveryOrderComponents.size(); i++) {
					DeliveryOrderComponent deliveryOrderComponent = deliveryOrderComponents.get(i);
					
					deliveryOrderComponent.setDeliveryOrder(deliveryOrder);
					
					if(deliveryOrderComponent.getDeliveryOrderComponentId()==null)
					{
						if(deliveryOrderComponent.getAssetModel()==null)
							logger.info("Nothing to do");
						else
						{
							PurchaseOrderDetail purchaseOrderDetail = purchaseOrder.findPurchaseOrderDetails(deliveryOrderComponent.getAssetModel());
							if(purchaseOrderDetail!=null)
							{
								deliveryOrderComponent.setUnitPrice(purchaseOrderDetail.getUnitPrice());
								deliveryOrderComponent.setAssetTag(getNextAssetTag(AssetModel.ASSETTYPE_COMPONENT, deliveryOrderComponent.getAssetModel().getAssetModelCode()));
								//deliveryOrderComponent.setAssetTag("DUMMY-TAG");
								em.persist(deliveryOrderComponent);
							}
							else
								logger.warn("Asset Model " +deliveryOrderComponent.getAssetModel()+ " is not available in PO items");
						}
					}
					else
					{
						if(deliveryOrderComponent.isDeleted())
						{
							deliveryOrderComponent = em.getReference(DeliveryOrderComponent.class,deliveryOrderComponent.getDeliveryOrderComponentId());
							em.remove(deliveryOrderComponent);
						}
						else
							em.merge(deliveryOrderComponent);
					}
				}
			}
		}
	}

	public void deleteDeliveryOrder(Integer deliveryOrderId) {
		// TODO Auto-generated method stub
		
	}
	
	
	public static class DeliveryOrderPaginationHelper extends PaginationHelper<DeliveryOrder> {

		@Override
		public String[] getValidColumnName() {
			return new String[]{"deliveryOrderCode","deliveryOrderDate","purchaseOrder.purchaseOrderCode","deliveryOrder.accessDepartmentId"};
		}
		
		@Override
		public String[] getValidSortName() {
			return new String[]{"deliveryOrderCode","deliveryOrderDate","purchaseOrder.purchaseOrderCode",
					"purchaseOrder.purchaseOrderDate","purchaseOrder.purchaseOrderType","modifiedBy","dateModified"};
		}

		@Override
		public String getDefaultSortName() {
			return "deliveryOrderDate";
		}
		
		@Override
		public QueryHelper getQueryHelper() {
			QueryHelper queryHelper = new QueryHelper();
			queryHelper.select("select deliveryOrder from DeliveryOrder deliveryOrder");
			return queryHelper;
		}
	}

}
